import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor;

public class RunLengthEncoding
{
	public static String encode(String source)
	{
		StringBuffer dest = new StringBuffer();
		for (int i = 0; i < source.length(); i++)
		{
			int runLength = 1;
			while (i + 1 < source.length() && source.charAt(i) == source.charAt(i + 1))
			{
				runLength++;
				i++;
			}
			dest.append(runLength);
			dest.append(source.charAt(i));
		}
		return dest.toString();
	}

	public static String decode(String source)
	{
		StringBuffer dest = new StringBuffer();
		Pattern pattern = Pattern.compile("[0-9]+|[a-zA-Z]");
		Matcher matcher = pattern.matcher(source);
		while (matcher.find())
		{
			int number = Integer.parseInt(matcher.group());
			matcher.find();
			while (number-- != 0)
			{
				dest.append(matcher.group());
			}
		}
		return dest.toString();
	}

	public static byte[] compress(byte srcBytes[]) throws IOException
	{
		LZ4Factory factory = LZ4Factory.fastestInstance();
		ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
		LZ4Compressor compressor = factory.fastCompressor();
		LZ4BlockOutputStream compressedOutput = new LZ4BlockOutputStream(byteOutput, 2048, compressor);
		compressedOutput.write(srcBytes);
		compressedOutput.close();
		return byteOutput.toByteArray();
	}

	public static byte[] uncompress(byte[] bytes) throws IOException
	{
		LZ4Factory factory = LZ4Factory.fastestInstance();
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		LZ4FastDecompressor decompresser = factory.fastDecompressor();
		LZ4BlockInputStream lzis = new LZ4BlockInputStream(new ByteArrayInputStream(bytes), decompresser);
		int count;
		byte[] buffer = new byte[2048];
		while ((count = lzis.read(buffer)) != -1)
		{
			baos.write(buffer, 0, count);
		}
		lzis.close();
		return baos.toByteArray();
	}

	public static String toHexString(byte[] bytes)
	{
		StringBuilder sb = new StringBuilder();
		for (byte b : bytes)
		{
			sb.append(String.format("%02X", b));
		}
		return sb.toString();
	}

	// 压缩
	public static String zipString(String unzip)
	{
		Deflater deflater = new Deflater(9); // 0 ~ 9 压缩等级 低到高
		deflater.setInput(unzip.getBytes());
		deflater.finish();

		final byte[] bytes = new byte[256];
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);

		while (!deflater.finished())
		{
			int length = deflater.deflate(bytes);
			outputStream.write(bytes, 0, length);
		}

		deflater.end();
		return new sun.misc.BASE64Encoder().encodeBuffer(outputStream.toByteArray());
	}

	// 解压缩
	public static String unzipString(String zip) throws IOException
	{
		byte[] decode = new sun.misc.BASE64Decoder().decodeBuffer(zip);

		Inflater inflater = new Inflater();
		inflater.setInput(decode);

		final byte[] bytes = new byte[256];
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);

		try
		{
			while (!inflater.finished())
			{
				int length = inflater.inflate(bytes);
				outputStream.write(bytes, 0, length);
			}
		}
		catch (DataFormatException e)
		{
			e.printStackTrace();
			return null;
		}
		finally
		{
			inflater.end();
		}

		return outputStream.toString();
	}

	public static void main(String[] args) throws IOException
	{
		String s = "{\"ad\": \"faf\",\"ad\": \"faf\"\"ad\": \"faf\"\"ad\": \"faf\"\"ad\": \"faf\"\"ad\": \"faf\"\"ad\": \"faf\"\"ad\": \"faf\"}";
		System.err.println(zipString(s));
		System.err.println(unzipString(zipString(s)));
	}

}
